package report

import (
	"database/sql"
	"github.com/astaxie/beego/orm"
	"io"
	"io/ioutil"
	"nspel/entity"
	"nspel/lock"
	"nspel/nspel"
	"nspel/util"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
)

var reportTaskState bool

var convertPdfCommand string

func init() {
	reportTaskState = false
	switch runtime.GOOS {
	case "windows":
		convertPdfCommand = "soffice"
		break
	case "linux":
		convertPdfCommand = "libreoffice6.1"
		break
	}
}

func setTaskStateToFalse() {
	if reportTaskState == true {
		reportTaskState = false
	}
}

func ReportTask() {
	if reportTaskState == false {
		reportTaskState = true
	} else {
		return
	}
	defer setTaskStateToFalse()
	report := new(entity.Report)
	var reports []entity.Report
	lock.PhysicalImportLock.Lock()
	_, err := nspel.GetOrmEngine().QueryTable(report.TableName()).Filter("is_update", true).Filter("type__in", 1, 2).All(&reports)
	lock.PhysicalImportLock.Unlock()
	if err != nil {
		nspel.Error(err)
		return
	}
	zipMap := make(map[string][][]string)
	for i := 0; i < len(reports); i++ {
		nspel.Info(reports[i].Info.String + "-----" + reports[i].Tip.String)
		if reports[i].Type == 1 {
			err = GenerateSchoolReport(reports[i].SchoolId, reports[i].MeasureTime)
			if err != nil {
				nspel.Error(err)
			}
			copyFile(&reports[i], &zipMap)
		} else if reports[i].Type == 2 {
			err = GenerateGradeReport(&reports[i])
			if err != nil {
				nspel.Error(err)
			}
			copyFile(&reports[i], &zipMap)
		}
	}
	createZip(&zipMap)
	nspel.RedisKeyDelete("report")
}

func convertToPdf(path string) error {
	dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		return err
	}
	fileName := path[strings.LastIndex(path, "/")+1:]
	cmd := exec.Command(convertPdfCommand, "--headless", "--invisible", "--convert-to", "pdf", fileName)
	cmd.Dir = dir + path[1:strings.LastIndex(path, "/")]
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		nspel.Error(err)
		return err
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		nspel.Error(err)
		return err
	}
	err = cmd.Start()
	if err != nil {
		nspel.Error(err)
		return err
	}
	go logStdout(&stdout)
	go logStderr(&stderr)
	return cmd.Wait()
}

func copyFile(report *entity.Report, zipMap *map[string][][]string) error {
	province := new(entity.Province)
	err := province.SelectAllById(report.ProvinceId)
	if err != nil {
		nspel.Error(err)
		return err
	}
	city := new(entity.City)
	err = city.SelectAllById(report.CityId)
	if err != nil {
		nspel.Error(err)
		return err
	}
	area := new(entity.Area)
	err = area.SelectAllById(report.ExperimentAreaId)
	if err != nil {
		nspel.Error(err)
		return err
	}
	school := new(entity.School)
	err = school.SelectAllById(report.SchoolId)
	if err != nil {
		nspel.Error(err)
		return err
	}
	err = os.MkdirAll("./reportFiles"+"/"+province.Name+"/"+city.Name+"/"+area.Name+"/"+school.Name+"/"+report.MeasureTime.Format("2006-01-02"), os.ModePerm)
	if err != nil {
		nspel.Error(err)
		return err
	}
	if report.Type == 1 {
		rd, err := ioutil.ReadDir("./pptx-reports/reports/output")
		if err != nil {
			nspel.Error(err)
			return err
		}
		for _, fi := range rd {
			if !fi.IsDir() {
				source, err := os.Open("./pptx-reports/reports/output/" + fi.Name())
				if err != nil {
					nspel.Error(err)
					source.Close()
					return err
				}
				destination, err := os.Create("./reportFiles" + "/" + province.Name + "/" + city.Name + "/" + area.Name + "/" + school.Name + "/" + report.MeasureTime.Format("2006-01-02") + "/" + fi.Name())
				if err != nil {
					nspel.Error(err)
					source.Close()
					destination.Close()
					return err
				}
				_, err = io.Copy(destination, source)
				if err != nil {
					nspel.Error(err)
					source.Close()
					destination.Close()
					return err
				}
				source.Close()
				destination.Close()
				err = os.Remove("./pptx-reports/reports/output/" + fi.Name())
				if err != nil {
					nspel.Error(err)
					return err
				}
				measureTime := report.MeasureTime.Format("2006-01-02")
				fileName := fi.Name()[:strings.Index(fi.Name(), ".")]
				(*zipMap)["./reportFiles"+"/"+province.Name+"/"+city.Name+"/"+area.Name+"/"+school.Name+"/"+measureTime+"/"+fileName] = [][]string{
					{"/reportFiles",
						province.Name + ".zip",
						province.Name + "/" + city.Name + "/" + area.Name + "/" + school.Name + "/" + measureTime + "/" + fileName + ".pptx"},
					{"/reportFiles",
						province.Name + "pdf.zip",
						province.Name + "/" + city.Name + "/" + area.Name + "/" + school.Name + "/" + measureTime + "/" + fileName + ".pdf"},
					{"/reportFiles" + "/" + province.Name,
						city.Name + ".zip",
						city.Name + "/" + area.Name + "/" + school.Name + "/" + measureTime + "/" + fileName + ".pptx"},
					{"/reportFiles" + "/" + province.Name,
						city.Name + "pdf.zip",
						city.Name + "/" + area.Name + "/" + school.Name + "/" + measureTime + "/" + fileName + ".pdf"},
					{"/reportFiles" + "/" + province.Name + "/" + city.Name,
						area.Name + ".zip",
						area.Name + "/" + school.Name + "/" + measureTime + "/" + fileName + ".pptx"},
					{"/reportFiles" + "/" + province.Name + "/" + city.Name,
						area.Name + "pdf.zip",
						area.Name + "/" + school.Name + "/" + measureTime + "/" + fileName + ".pdf"},
					{"/reportFiles" + "/" + province.Name + "/" + city.Name + "/" + area.Name,
						school.Name + ".zip",
						school.Name + "/" + measureTime + "/" + fileName + ".pptx"},
					{"/reportFiles" + "/" + province.Name + "/" + city.Name + "/" + area.Name,
						school.Name + "pdf.zip",
						school.Name + "/" + measureTime + "/" + fileName + ".pdf"}}
				report.Url = sql.NullString{String: "./reportFiles" + "/" + province.Name + "/" + city.Name + "/" + area.Name + "/" + school.Name + "/" + measureTime + "/" + fileName + ".pptx", Valid: true}
				err = convertToPdf(report.Url.String)
				if err != nil {
					return err
				}
				report.IsUpdate = false
				err = report.Update("url", "is_update")
				if err != nil {
					nspel.Error(err)
					return err
				}
			}
		}
	} else if report.Type == 2 {
		rd, err := ioutil.ReadDir("./pptx-reports/reports/output")
		if err != nil {
			nspel.Error(err)
			return err
		}
		score := new(entity.Score)
		var list orm.ParamsList
		_, err = nspel.GetOrmEngine().QueryTable(score.TableName()).Filter("measurement_date", report.MeasureTime.Format("2006-01-02")).Filter("grade", report.Info.String).Distinct().ValuesFlat(&list, "class")
		for _, fi := range rd {
			if !fi.IsDir() {
				measureTime := report.MeasureTime.Format("2006-01-02")
				fileName := fi.Name()[:strings.Index(fi.Name(), ".")]
				source, err := os.Open("./pptx-reports/reports/output/" + fi.Name())
				if err != nil {
					nspel.Error(err)
					source.Close()
					continue
				}
				destination, err := os.Create("./reportFiles" + "/" + province.Name + "/" + city.Name + "/" + area.Name + "/" + school.Name + "/" + measureTime + "/" + fileName + ".pptx")
				if err != nil {
					nspel.Error(err)
					source.Close()
					destination.Close()
					continue
				}
				_, err = io.Copy(destination, source)
				if err != nil {
					nspel.Error(err)
					source.Close()
					destination.Close()
					continue
				}
				source.Close()
				destination.Close()
				err = os.Remove("./pptx-reports/reports/output/" + fi.Name())
				if err != nil {
					nspel.Error(err)
					continue
				}
				(*zipMap)["./reportFiles"+"/"+province.Name+"/"+city.Name+"/"+area.Name+"/"+school.Name+"/"+measureTime+"/"+fileName] = [][]string{
					{"/reportFiles",
						province.Name + ".zip",
						province.Name + "/" + city.Name + "/" + area.Name + "/" + school.Name + "/" + measureTime + "/" + fileName + ".pptx"},
					{"/reportFiles",
						province.Name + "pdf.zip",
						province.Name + "/" + city.Name + "/" + area.Name + "/" + school.Name + "/" + measureTime + "/" + fileName + ".pdf"},
					{"/reportFiles" + "/" + province.Name,
						city.Name + ".zip",
						city.Name + "/" + area.Name + "/" + school.Name + "/" + measureTime + "/" + fileName + ".pptx"},
					{"/reportFiles" + "/" + province.Name,
						city.Name + "pdf.zip",
						city.Name + "/" + area.Name + "/" + school.Name + "/" + measureTime + "/" + fileName + ".pdf"},
					{"/reportFiles" + "/" + province.Name + "/" + city.Name,
						area.Name + ".zip",
						area.Name + "/" + school.Name + "/" + measureTime + "/" + fileName + ".pptx"},
					{"/reportFiles" + "/" + province.Name + "/" + city.Name,
						area.Name + "pdf.zip",
						area.Name + "/" + school.Name + "/" + measureTime + "/" + fileName + ".pdf"},
					{"/reportFiles" + "/" + province.Name + "/" + city.Name + "/" + area.Name,
						school.Name + ".zip",
						school.Name + "/" + measureTime + "/" + fileName + ".pptx"},
					{"/reportFiles" + "/" + province.Name + "/" + city.Name + "/" + area.Name,
						school.Name + "pdf.zip",
						school.Name + "/" + measureTime + "/" + fileName + ".pdf"}}
				err = convertToPdf("./reportFiles" + "/" + province.Name + "/" + city.Name + "/" + area.Name + "/" + school.Name + "/" + measureTime + "/" + fileName + ".pptx")
				if err != nil {
					return err
				}
				if strings.Index(fi.Name(), "班") != -1 {
					class := fi.Name()[strings.LastIndex(fi.Name(), "-")+1 : strings.LastIndex(fi.Name(), "班")]
					if len(class) == 1 {
						class = "0" + class
					}
					for i := 0; i < len(list); i++ {
						if list[i].(string)[5:7] == class {
							var cReport entity.Report
							err := nspel.GetOrmEngine().QueryTable(report.TableName()).Filter("school_id", report.SchoolId).Filter("measure_time", report.MeasureTime.Format("2006-01-02")).Filter("info", list[i]).One(&cReport)
							if err != nil {
								nspel.Error(err)
								continue
							}
							cReport.Url = util.GetNullString("./reportFiles" + "/" + province.Name + "/" + city.Name + "/" + area.Name + "/" + school.Name + "/" + measureTime + "/" + fileName + ".pptx")
							cReport.IsUpdate = false
							err = cReport.Update("url", "is_update")
							if err != nil {
								nspel.Error(err)
								continue
							}
							break
						}
					}
				} else {
					report.Url = util.GetNullString("./reportFiles" + "/" + province.Name + "/" + city.Name + "/" + area.Name + "/" + school.Name + "/" + measureTime + "/" + fileName + ".pptx")
					report.IsUpdate = false
					err = report.Update("url", "is_update")
					if err != nil {
						nspel.Error(err)
						continue
					}
				}
			}
		}
	}
	return nil
}

/*
func createZip(zipMap *map[string]byte, fileNameAdd string, suffix string) error {
	for k := range *zipMap {
		d, err := os.Create(k + fileNameAdd + ".zip")
		if err != nil {
			nspel.Error(err)
			continue
		}
		w := zip.NewWriter(d)
		f, err := os.Open(k)
		if err != nil {
			nspel.Error(err)
			continue
		}
		err = compress(f, "", w, suffix)
		if err != nil {
			nspel.Error(err)
			continue
		}
		w.Close()
		d.Close()
	}
	return nil
}*/

func createZip(zipMap *map[string][][]string) error {
	for _, v := range *zipMap {
		for i := 0; i < len(v); i++ {
			err := compressScript(v[i][1], v[i][2], v[i][0])
			if err != nil {
				nspel.Error(err)
				continue
			}
		}
	}
	return nil
}

func compressScript(zipName string, filePath string, subDir string) error {
	dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		nspel.Error(err)
		return err
	}
	cmd := exec.Command("zip", "-r", "-u", "-9", zipName, filePath)
	cmd.Dir = dir + subDir
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		nspel.Error(err)
		return err
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		nspel.Error(err)
		return err
	}
	err = cmd.Start()
	if err != nil {
		nspel.Error(err)
		return err
	}
	go logStdout(&stdout)
	go logStderr(&stderr)
	return cmd.Wait()
}

/*
func compress(file *os.File, prefix string, zw *zip.Writer, suffix string) error {
	info, err := file.Stat()
	if err != nil {
		return err
	}
	if info.IsDir() {
		prefix = prefix + "/" + info.Name()
		fileInfos, err := file.Readdir(-1)
		if err != nil {
			return err
		}
		for _, fi := range fileInfos {
			f, err := os.Open(file.Name() + "/" + fi.Name())
			if err != nil {
				return err
			}
			err = compress(f, prefix, zw, suffix)
			if err != nil {
				return err
			}
		}
	} else if strings.LastIndex(info.Name(), suffix) == len(info.Name())-len(suffix) {
		header, err := zip.FileInfoHeader(info)
		header.Name = prefix[1:] + "/" + header.Name
		if err != nil {
			return err
		}
		header.Method = zip.Deflate
		writer, err := zw.CreateHeader(header)
		if err != nil {
			return err
		}
		_, err = io.Copy(writer, file)
		file.Close()
		if err != nil {
			return err
		}
	}
	return nil
}
*/
